

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/fluid.png">
  <link rel="icon" href="/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="叶飞">
  <meta name="keywords" content="">
  
    <meta name="description" content="一、TCP基本认识    序列号：  初始值：建立连接时为随机数，通过SYN包发给接收端主机 解决网络包乱序的问题   确认应答号：【期望下一次收到的数据序列号，一般是当前收到的数据包序列号+1】  解决丢包问题   控制位  ACK: 1表示确认应答字段有效；    SYN包可以不为1，其余必须为1 RST: reset,  1表示连接重置，异常，连接断开 SYN：同步   1表示希望建立连接">
<meta property="og:type" content="article">
<meta property="og:title" content="TCP握手">
<meta property="og:url" content="http://example.com/2022/04/02/TCP%E6%8F%A1%E6%89%8B/index.html">
<meta property="og:site_name" content="博客">
<meta property="og:description" content="一、TCP基本认识    序列号：  初始值：建立连接时为随机数，通过SYN包发给接收端主机 解决网络包乱序的问题   确认应答号：【期望下一次收到的数据序列号，一般是当前收到的数据包序列号+1】  解决丢包问题   控制位  ACK: 1表示确认应答字段有效；    SYN包可以不为1，其余必须为1 RST: reset,  1表示连接重置，异常，连接断开 SYN：同步   1表示希望建立连接">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/1.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/5.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/6.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/7.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/2.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/8.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/9.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/10.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/11.jp">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/12.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/13.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/14.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/15.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/3.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/16.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/17.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/18.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/4.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/19.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/20.jpg">
<meta property="og:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/21.jpg">
<meta property="article:published_time" content="2022-04-02T07:40:05.000Z">
<meta property="article:modified_time" content="2022-04-02T07:49:54.536Z">
<meta property="article:author" content="叶飞">
<meta property="article:tag" content="面试">
<meta property="article:tag" content="TCP">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="http://example.com/img/TCP%E6%8F%A1%E6%89%8B.assets/1.jpg">
  
  
  
  <title>TCP握手 - 博客</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"example.com","root":"/","version":"1.9.2","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.2.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>No Tomorrow</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="TCP握手"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        叶飞
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-04-02 15:40" pubdate>
          星期六, 四月 2日 2022, 3:40 下午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          <!-- compatible with older versions-->
          9.3k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          <!-- compatible with older versions-->
          78 分钟
        
      </span>
    

    
    
      
        <span id="leancloud-page-views-container" class="post-meta" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="leancloud-page-views"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">TCP握手</h1>
            
            
              <div class="markdown-body">
                
                <h1 id="一-tcp基本认识"><a class="markdownIt-Anchor" href="#一-tcp基本认识"></a> 一、TCP基本认识</h1>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/1.jpg" srcset="/img/loading.gif" lazyload alt></p>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/5.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>序列号：
<ul>
<li>初始值：建立连接时为随机数，通过SYN包发给接收端主机</li>
<li>解决网络包乱序的问题</li>
</ul>
</li>
<li>确认应答号：【期望下一次收到的数据序列号，一般是当前收到的数据包序列号+1】
<ul>
<li>解决丢包问题</li>
</ul>
</li>
<li>控制位
<ul>
<li>ACK: 1表示确认应答字段有效；    SYN包可以不为1，其余必须为1</li>
<li>RST: reset,  1表示连接重置，异常，连接断开</li>
<li>SYN：同步   1表示希望建立连接，对应的序列号字段初始化</li>
<li>FIN： 1表示希望断开连接</li>
</ul>
</li>
</ul>
<h2 id="1-为什么需要-tcp-协议-tcp-工作在哪一层"><a class="markdownIt-Anchor" href="#1-为什么需要-tcp-协议-tcp-工作在哪一层"></a> 1 为什么需要 TCP 协议？ TCP 工作在哪一层？</h2>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/6.jpg" srcset="/img/loading.gif" lazyload alt></p>
<p>（1）IP 层是「不可靠」的，它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。</p>
<p>​	IP层位于网络层</p>
<p>如果需要保障网络数据包的可靠性，那么就需要由上层（传输层）的 TCP 协议来负责。</p>
<p>（2）TCP位于传输层，提高可靠的传输</p>
<p>因为 TCP 是一个工作在传输层的可靠数据传输的服务，它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。</p>
<h2 id="2-什么是tcp"><a class="markdownIt-Anchor" href="#2-什么是tcp"></a> 2 什么是TCP</h2>
<p>TCP 是<code>面向连接的</code>、<code>可靠的</code>、<code>基于字节流</code>的传输层通信协议。</p>
<ul>
<li>面向连接：一对一的连接传输</li>
<li>可靠：无论链路怎么变化，都保证报文一定可以到达接收端</li>
<li>基于字节流：消息是「没有边界」的，所以无论我们消息有多大都可以进行传输。并且消息是「有序的」，当「前一个」消息没有收到的时候，即使它先收到了后面的字节，那么也不能扔给应用层去处理，同时对「重复」的报文会自动丢弃。</li>
</ul>
<h2 id="3-什么是tcp连接"><a class="markdownIt-Anchor" href="#3-什么是tcp连接"></a> 3 什么是TCP连接</h2>
<figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs tex">用于保证可靠性和流量控制维护的某些[状态信息]，这些信息的组合，包括[Socket]、[序列号]和[窗口大小]称为连接。<br></code></pre></td></tr></table></figure>
<ul>
<li>Socket：【IP:端口】</li>
<li>序列号：解决乱序</li>
<li>窗口大小：流量控制、拥塞控制</li>
</ul>
<h2 id="4-如何确定一个tcp连接"><a class="markdownIt-Anchor" href="#4-如何确定一个tcp连接"></a> 4 如何确定一个TCP连接</h2>
<p>【TCP四元组】</p>
<ul>
<li>源地址
<ul>
<li>IP头部；32位</li>
</ul>
</li>
<li>源端口
<ul>
<li>TCP头部；16位</li>
</ul>
</li>
<li>目的地址
<ul>
<li>IP头部；32位</li>
</ul>
</li>
<li>目的端口
<ul>
<li>TCP头部；16位</li>
</ul>
</li>
</ul>
<h2 id="5-有一个-ip-的服务器监听了一个端口它的-tcp-的最大连接数是多少"><a class="markdownIt-Anchor" href="#5-有一个-ip-的服务器监听了一个端口它的-tcp-的最大连接数是多少"></a> 5 有一个 IP 的服务器监听了一个端口，它的 TCP 的最大连接数是多少？</h2>
<p><code>服务器</code>通常固定在某个本地端口上监听，等待客户端的连接请求。</p>
<p>因此，<code>客户端 IP 和 端口</code>是可变的，其理论值计算公式如下:<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>M</mi><mi>a</mi><mi>x</mi><mi>T</mi><mi>C</mi><mi>P</mi><mi>N</mi><mi>u</mi><mi>m</mi><mo>=</mo><mi mathvariant="normal">客</mi><mi mathvariant="normal">户</mi><mi mathvariant="normal">端</mi><mi>I</mi><mi>P</mi><mi mathvariant="normal">数</mi><mo>∗</mo><mi mathvariant="normal">客</mi><mi mathvariant="normal">户</mi><mi mathvariant="normal">端</mi><mi mathvariant="normal">端</mi><mi mathvariant="normal">口</mi><mi mathvariant="normal">数</mi></mrow><annotation encoding="application/x-tex">MaxTCPNum = 客户端IP数 * 客户端端口数</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.10903em;">M</span><span class="mord mathdefault">a</span><span class="mord mathdefault">x</span><span class="mord mathdefault" style="margin-right:0.13889em;">T</span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span><span class="mord mathdefault">u</span><span class="mord mathdefault">m</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord cjk_fallback">客</span><span class="mord cjk_fallback">户</span><span class="mord cjk_fallback">端</span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord cjk_fallback">数</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0em;vertical-align:0em;"></span><span class="mord cjk_fallback">客</span><span class="mord cjk_fallback">户</span><span class="mord cjk_fallback">端</span><span class="mord cjk_fallback">端</span><span class="mord cjk_fallback">口</span><span class="mord cjk_fallback">数</span></span></span></span></p>
<p>​	IPV4: IP 32位， 端口号16位   所以理论是<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mn>48</mn></msup></mrow><annotation encoding="application/x-tex">2^{48}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">4</span><span class="mord mtight">8</span></span></span></span></span></span></span></span></span></span></span></span></p>
<p><code>服务端</code>最大<code>并发 TCP</code>连接数远不能达到理论上限。</p>
<ul>
<li>首先主要是文<code>件描述符限制</code>，Socket 都是文件，所以首先要通过 <code>ulimit</code> 配置文件描述符的数目；</li>
<li>另一个是<code>内存限制</code>，每个 TCP 连接都要占用一定内存，操作系统的内存是有限的。</li>
</ul>
<h2 id="6-tcp和udp的区别"><a class="markdownIt-Anchor" href="#6-tcp和udp的区别"></a> 6 TCP和UDP的区别</h2>
<p>（1）UDP</p>
<p>​	UDP 不提供复杂的控制机制，利用 IP 提供面向「无连接」的通信服务。</p>
<ul>
<li>校验和：为了提供可靠的 UDP 首部和数据而设计。    【数据可靠性】</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/7.jpg" srcset="/img/loading.gif" lazyload alt></p>
<p>（2）TCP和UDP区别</p>
<figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs tex">1. 连接<br>- TCP 是面向连接的传输层协议，传输数据前先要建立连接。<br>- UDP 是不需要连接，即刻传输数据。<br>2. 服务对象<br>- TCP 是一对一的两点服务，即一条连接只有两个端点。<br>- UDP 支持一对一、一对多、多对多的交互通信<br>3. 可靠性<br>- TCP 是可靠交付数据的，数据可以无差错、不丢失、不重复、按需到达。<br>- UDP 是【尽最大努力】交付，不保证可靠交付数据。<br>4. 拥塞控制、流量控制<br>- TCP 有【拥塞控制和流量控制】机制，保证数据传输的安全性。<br>- UDP 则没有，即使网络非常拥堵了，也不会影响 UDP 的发送速率。<br>5. 首部开销<br>- TCP 首部长度较长，会有一定的开销，首部在没有使用「选项」字段时是 【20】 个字节，如果使用了「选项」字段则会变长的。<br>- UDP 首部只有【 8 】个字节，并且是固定不变的，开销较小。<br>6. 传输方式<br>- TCP 是流式传输，没有边界，但保证顺序和可靠。<br>- UDP 是一个包一个包的发送，是【有边界】的，但可能会丢包和乱序。<br>7. 分片不同<br>- TCP 的数据大小如果大于 MSS 大小，则会【在传输层进行分片】，目标主机收到后，也同样在传输层组装 TCP 数据包，如果中途丢失了一个分片，只需要传输丢失的这个分片。<br>- UDP 的数据大小如果大于 MTU 大小，则会在 【IP 层进行分片】，目标主机收到后，在 IP 层组装完数据，接着再传给传输层，但是如果中途丢了一个分片，则就需要重传所有的数据包，这样传输效率非常差，所以通常 UDP 的报文应该小于 MTU。<br></code></pre></td></tr></table></figure>
<p>（3）TCP 和 UDP 应用场景：</p>
<ul>
<li>TCP
<ul>
<li><code>FTP</code> 文件传输</li>
<li><code>HTTP</code> / <code>HTTPS</code></li>
</ul>
</li>
<li>UDP- 简单高效
<ul>
<li>包总量较少的通信，如<code>DNS</code>、<code>SNMP</code> 等</li>
<li>视频、音频等多媒体通信</li>
<li><code>广播</code>通信</li>
</ul>
</li>
</ul>
<h2 id="7-为什么-udp-头部没有首部长度字段而-tcp-头部有首部长度字段呢"><a class="markdownIt-Anchor" href="#7-为什么-udp-头部没有首部长度字段而-tcp-头部有首部长度字段呢"></a> 7 为什么 UDP 头部没有「首部长度」字段，而 TCP 头部有「首部长度」字段呢？</h2>
<p>原因是 TCP 有<code>可变长</code>的「选项」字段，而 UDP 头部长度(<code>固定8个字节</code>)则是<code>不会变化</code>的，无需多一个字段去记录UDP 的首部长度。</p>
<h2 id="8-为什么-udp-头部有包长度字段而-tcp-头部则没有包长度字段呢"><a class="markdownIt-Anchor" href="#8-为什么-udp-头部有包长度字段而-tcp-头部则没有包长度字段呢"></a> 8 为什么 UDP 头部有「包长度」字段，而 TCP 头部则没有「包长度」字段呢？</h2>
<ul>
<li>
<p>因为为了网络设备硬件设计和处理方便，首部长度需要是 4 字节的整数倍。  【凑整】</p>
<ul>
<li>如果去掉 UDP 「包长度」字段，那 UDP 首部长度就不是 4 字节的整数倍了，所以这可能是为了补全 UDP 首部长度是 4 字节的整数倍，才补充了「包长度」字段</li>
</ul>
</li>
<li>
<p>TCP数据长度/数据负载可以计算的</p>
<ul>
<li>TCP数据长度=IP总长度-IP首部长度-TCP首部长度</li>
<li>IP首部格式给出了IP总长和首部长度</li>
<li>TCP首部给出首部长度（4位)</li>
</ul>
</li>
</ul>
<h1 id="二-tcp连接建立"><a class="markdownIt-Anchor" href="#二-tcp连接建立"></a> 二、TCP连接建立</h1>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/2.jpg" srcset="/img/loading.gif" lazyload alt></p>
<h2 id="1-tcp-三次握手过程和状态变迁"><a class="markdownIt-Anchor" href="#1-tcp-三次握手过程和状态变迁"></a> 1 TCP 三次握手过程和状态变迁</h2>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/8.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>1 一开始，客户端和服务端都处于 <code>CLOSED</code> 状态。先是服务端主动监听某个端口，处于<code>LISTEN</code>状态</li>
<li>2 <code>客户端</code>SYN报文
<ul>
<li>序列号：随机初始化</li>
<li>SYN=1</li>
<li>进入<code>SYN-SENT</code>态</li>
</ul>
</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/9.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>3 <code>服务端</code>接收SYN报文
<ul>
<li>序列号：随机</li>
<li>确认应答号：客户端序列号+1</li>
<li>SYN=1   ACK=1</li>
<li>进入<code>SYN-RCVD</code></li>
</ul>
</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/10.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>
<p>4 <code>客户端</code>  ACK</p>
<ul>
<li>ACK=1</li>
<li>「确认应答号」字段填入 <code>server_isn + 1</code></li>
<li>客户端处于 <code>ESTABLISHED</code></li>
</ul>
<p>(<img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/11.jp" srcset="/img/loading.gif" lazyload alt></p>
</li>
<li>
<p>5 <code>服务端</code>ACK</p>
<ul>
<li><code>ESTABLISHED 状态</code></li>
</ul>
</li>
</ul>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">第三次握手是可以携带数据的，前两次握手是不可以携带数据的<br></code></pre></td></tr></table></figure>
<h2 id="2-如何在-linux-系统中查看-tcp-状态"><a class="markdownIt-Anchor" href="#2-如何在-linux-系统中查看-tcp-状态"></a> 2 如何在 Linux 系统中查看 TCP 状态？</h2>
<p><code>netstat-napt</code></p>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/12.jpg" srcset="/img/loading.gif" lazyload alt></p>
<h2 id="3-为什么是三次握手不是两次-四次"><a class="markdownIt-Anchor" href="#3-为什么是三次握手不是两次-四次"></a> 3 为什么是三次握手？不是两次、四次？</h2>
<p>重要的是<code>为什么三次握手才可以初始化Socket、序列号和窗口大小并建立 TCP 连接。</code><br>
接下来以三个方面分析三次握手的原因：</p>
<ul>
<li>
<p>三次握手才可以阻止重复历史连接的初始化（主要原因）</p>
<ul>
<li>三次握手的首要原因是为了<code>防止旧的重复连接初始化造成混乱</code>。</li>
<li>旧的SYN报文可能先到达
<ul>
<li><code>客户端</code>收到后可以根据自身的上下文，判断这是一个历史连接（序列号过期或超时），那么客户端就会发送<code>RST 报文</code>给<code>服务端</code>，表示中止这一次连接。</li>
<li>两次握手则无法判断当前连接是否是历史连接。因为服务端第一次接收SYN报文，则认为连接建立，而这可能不是客户端想要的。</li>
<li>场景：客户端先发送了一个SYN连接，觉得不妥，又发起一个。结果服务端先收到旧的，然后就确认了旧的。</li>
</ul>
</li>
<li>其次，服务端的序列号也没法确认，导致服务端不确定客户端是否同意建立连接</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/13.jpg" srcset="/img/loading.gif" lazyload alt></p>
</li>
<li>
<p>三次握手才可以同步双方的初始序列号</p>
<ul>
<li>序列号作为可靠传输的一个关键因素
<ul>
<li>去重</li>
<li>保证数据包顺序 重组完整数据</li>
<li>标识数据包哪些被接收（根据接收方的反馈）</li>
</ul>
</li>
<li>四次握手等价于三次握手
<ul>
<li><code>服务端</code>把ACK和SYN合并</li>
</ul>
</li>
</ul>
</li>
<li>
<p>三次握手才可以避免资源浪费</p>
<p>如果只有「两次握手」，当客户端的 SYN 请求连接在网络中阻塞，客户端没有接收到 ACK 报文，就会重新发送 SYN ，由于没有第三次握手，服务器不清楚客户端是否收到了自己发送的建立连接的ACK 确认信号，所以每收到一个 SYN 就只能先主动建立一个连接，这会造成什么情况呢？<br>
<code>如果客户端的 SYN 阻塞了，重复发送多次 SYN 报文，那么服务器在收到请求后就会建立多个冗余的无效链接，造成不必要的资源浪费。</code></p>
<ul>
<li>两次握手会造成消息滞留情况下，服务器重复接受无用的连接请求 SYN 报文，而造成重复分配资源。</li>
</ul>
</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/14.jpg" srcset="/img/loading.gif" lazyload alt></p>
<h4 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h4>
<p>TCP 建立连接时，通过三次握手能防止历史连接的建立，能减少双方不必要的资源开销，能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。<br>
不使用「两次握手」和「四次握手」的原因：</p>
<ul>
<li>「两次握手」：无法防止<strong>历史连接</strong>的建立，会造成双方资源的浪费，也无法可靠的同步双方序列号；</li>
<li>「四次握手」：三次握手就已经理论上最少可靠连接建立，所以不需要使用更多的通信次数。</li>
</ul>
<h2 id="4-为什么客户端和服务端的初始序列号-isn-是不相同的"><a class="markdownIt-Anchor" href="#4-为什么客户端和服务端的初始序列号-isn-是不相同的"></a> 4 为什么客户端和服务端的初始序列号 ISN 是不相同的？</h2>
<p>如果一个已经失效的连接被重用了，但是该旧连接的历史报文还残留在网络中，如果序列号相同，那么就无法分辨出该报文是不是历史报文，如果历史报文被新的连接接收了，则会产生数据错乱。<br>
所以，每次建立连接前重新初始化一个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。<br>
另一方面是为了安全性，防止黑客伪造的相同序列号的 TCP 报文被对方接收。</p>
<h2 id="5-初始序列号-isn-是如何随机产生的"><a class="markdownIt-Anchor" href="#5-初始序列号-isn-是如何随机产生的"></a> 5 初始序列号 ISN 是如何随机产生的？</h2>
<p>起始 <code>ISN</code>是基于时钟的，每 4 毫秒 + 1，转一圈要 4.55 个小时。<br>
RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法。<br>
<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>I</mi><mi>S</mi><mi>N</mi><mo>=</mo><mi>M</mi><mo>+</mo><mi>F</mi><mo stretchy="false">(</mo><mi>l</mi><mi>o</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>h</mi><mi>o</mi><mi>s</mi><mi>t</mi><mo separator="true">,</mo><mi>l</mi><mi>o</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>p</mi><mi>o</mi><mi>r</mi><mi>t</mi><mo separator="true">,</mo><mi>r</mi><mi>e</mi><mi>m</mi><mi>o</mi><mi>t</mi><mi>e</mi><mi>h</mi><mi>o</mi><mi>s</mi><mi>t</mi><mo separator="true">,</mo><mi>r</mi><mi>e</mi><mi>m</mi><mi>o</mi><mi>t</mi><mi>e</mi><mi>p</mi><mi>o</mi><mi>r</mi><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">ISN = M + F (localhost, localport, remotehost, remoteport)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span><span class="mord mathdefault">o</span><span class="mord mathdefault">c</span><span class="mord mathdefault">a</span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span><span class="mord mathdefault">h</span><span class="mord mathdefault">o</span><span class="mord mathdefault">s</span><span class="mord mathdefault">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span><span class="mord mathdefault">o</span><span class="mord mathdefault">c</span><span class="mord mathdefault">a</span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span><span class="mord mathdefault">p</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord mathdefault">h</span><span class="mord mathdefault">o</span><span class="mord mathdefault">s</span><span class="mord mathdefault">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord mathdefault">p</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">t</span><span class="mclose">)</span></span></span></span></p>
<ul>
<li>M 是一个计时器，这个计时器每隔 4 毫秒加 1。</li>
<li>F 是一个 <strong>Hash</strong> 算法，根据<strong>源 IP、目的 IP、源端口、目的端口</strong>生成一个随机数值。要保证Hash 算法不能被外部轻易推算得出，用 MD5 算法是一个比较好的选择。</li>
</ul>
<h2 id="6-既然-ip-层会分片为什么-tcp-层还需要-mss-呢"><a class="markdownIt-Anchor" href="#6-既然-ip-层会分片为什么-tcp-层还需要-mss-呢"></a> 6 既然 IP 层会分片，为什么 TCP 层还需要 MSS 呢？</h2>
<p>MSS: 除去 IP 和 TCP 头部之后，一个网络包所能容纳的 TCP 数据的最大长度；</p>
<p><code>等价问题：为什么TCP现在传输层分片？</code></p>
<ul>
<li>完全由IP分片的情况
<ul>
<li>丢失某个IP分片：IP没有超时重传策略。所以需要发送端的TCP重传整个数据</li>
</ul>
</li>
<li>传输层先分片
<ul>
<li>双方协商MSS值</li>
<li>形成的IP包小于MTU</li>
<li>丢失：重发时，只需重传某个TCP分片。MSS单位的大小</li>
</ul>
</li>
</ul>
<h2 id="7-什么是-syn-攻击如何避免-syn-攻击"><a class="markdownIt-Anchor" href="#7-什么是-syn-攻击如何避免-syn-攻击"></a> 7 什么是 SYN 攻击？如何避免 SYN 攻击？</h2>
<p>（1）SYN攻击——占用服务端资源</p>
<p>我们都知道 TCP 连接建立是需要三次握手，假设攻击者短时间伪造不同 IP 地址的 SYN 报文，服端每接收到一个 SYN 报文，就进入<code>SYN_RCVD</code> 状态，但服务端发送出去的 ACK + SYN 报文，无法得到未知 IP 主机的 ACK 应答，久而久之就会<code>占满服务端的 SYN 接收队列（未连接队列</code>），使得服务器不能为正常用户服务。</p>
<p>（2）避免SYN</p>
<ol>
<li>
<p>修改linux内核参数：控制队列大小和当队列满时应做什么处理。</p>
<ul>
<li>
<p>当网卡接收数据包的速度大于内核处理的速度时，会有一个队列保存这些数据包。控制该队列的最大值如下参数：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">net.core.netdev_max_backlog<br></code></pre></td></tr></table></figure>
</li>
<li>
<p>SYN_RCVD 状态连接的最大个数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c">net.ipv4.tcp_max_syn_backlog<br></code></pre></td></tr></table></figure>
</li>
<li>
<p>超出处理能时，对新的 SYN 直接回报 RST，丢弃连接：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">net.ipv4.tcp_abort_on_overflow<br></code></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li>
<p>[SYN]报文队列  和 [Accept]报文队列</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">net.ipv4.tcp_syncookies = 1<br></code></pre></td></tr></table></figure>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/15.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>当 「 SYN 队列」满之后，后续服务器收到 SYN 包，不进入「 SYN 队列」；</li>
<li>计算出一个 cookie 值，再以 SYN + ACK 中的「序列号」返回客户端，</li>
<li>服务端接收到客户端的应答报文时，服务器会检查这个 ACK 包的合法性。如果合法，<code>直接放入</code>到「 Accept 队列」。</li>
<li>最后应用通过调用 accpet() socket 接口，从「 Accept 队列」取出的连接。</li>
</ul>
</li>
</ol>
<h1 id="三-tcp连接断开"><a class="markdownIt-Anchor" href="#三-tcp连接断开"></a> 三、TCP连接断开</h1>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/3.jpg" srcset="/img/loading.gif" lazyload alt></p>
<h2 id="1-四次挥手和状态变迁"><a class="markdownIt-Anchor" href="#1-四次挥手和状态变迁"></a> 1 四次挥手和状态变迁</h2>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/16.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>每个方向都需要一个 FIN 和一个 ACK，因此通常被称为四次挥手。</li>
<li><code>主动</code>关闭连接的，才有<code>TIME_WAIT</code>状态。</li>
</ul>
<h2 id="2-为什么挥手需要四次"><a class="markdownIt-Anchor" href="#2-为什么挥手需要四次"></a> 2 为什么挥手需要四次？</h2>
<ul>
<li>关闭连接时，客户端向服务端发送 FIN 时，仅仅表示客户端不再发送数据了但是还能接收数<br>
据。</li>
<li>服务器收到客户端的 FIN 报文时，先回一个 ACK 应答报文，而服务端可能还有数据需要处理<br>
和发送，等服务端不再发送数据时，才发送 FIN 报文给客户端来表示同意现在关闭连接。</li>
</ul>
<p>从上面过程可知，服务端通常需要等待完成数据的发送和处理，所以服务端的 ACK 和 FIN 一般都会分开发送，从而比三次握手导致多了一次。</p>
<h2 id="3-为什么-time_wait-等待的时间是-2msl"><a class="markdownIt-Anchor" href="#3-为什么-time_wait-等待的时间是-2msl"></a> 3 为什么 TIME_WAIT 等待的时间是 2MSL？</h2>
<ul>
<li><code>MSL</code> 是 Maximum Segment Lifetime，报文最大生存时间，它是任何报文在网络上存在的最长时间，超过这个时间报文将被丢弃。</li>
<li>MSL 与 TTL 的区别： MSL 的单位是时间，而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间，以确保报文已被自然消亡。</li>
<li>TIME_WAIT 等待 2 倍的 MSL，比较合理的解释是：<code>网络中可能存在来自发送方的数据包，当这些发送方的数据包被接收方处理后又会向对方发送响应，所以一来一回需要等待 2 倍的时间</code></li>
<li>2MSL 的时间是从<code>客户端接收到 FIN 后发送 ACK</code> 开始计时的。</li>
</ul>
<h2 id="4-为什么需要-time_wait-状态"><a class="markdownIt-Anchor" href="#4-为什么需要-time_wait-状态"></a> 4 为什么需要 TIME_WAIT 状态？</h2>
<p>主动发起关闭连接的一方，才会有 TIME-WAIT 状态。<br>
需要 TIME-WAIT 状态，主要是两个原因：</p>
<ul>
<li>
<p>防止具有相同「四元组」的「旧」数据包被收到；</p>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/17.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>如上图黄色框框服务端在关闭连接之前发送的 SEQ = 301 报文，被网络延迟了。</li>
<li>这时有<code>相同端口的 TCP 连接被复用(客户端)</code>后，被延迟的 SEQ = 301 抵达了客户端，那么客户端是有可能正常接收这个过期的报文，这就会产生数据错乱等严重的问题。</li>
<li>经过 2MSL 这个时间，<code>足以让两个方向上的数据包都被丢弃</code>，使得原来连接的数据包在网络中都自然消失，再出现的数据包一定都是新建立连接所产生的。</li>
</ul>
</li>
<li>
<p>保证「被动关闭连接」的一方能被正确的关闭，即保证最后的 ACK 能让被动关闭方接收，从而帮助其正常关闭；</p>
<ul>
<li>红色框框客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了，此时如果客户端 <code>TIME-WAIT</code>过短或没有，则就直接进入了<code>CLOSED</code> 状态了，那么<code>服务端</code>则会一直处在 <code>LASE_ACK</code> 状态。</li>
<li>当客户端发起建立连接的 <code>SYN</code> 请求报文后，服务端会发送<code>RST</code> 报文给客户端，连接建立的过程就会被终止。</li>
</ul>
</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/18.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>如果TIME_WAIT时间足够长：  【保证正常关闭】
<ul>
<li>服务端正常收到四次挥手的最后一个 ACK 报文，则服务端正常关闭连接。</li>
<li>服务端没有收到四次挥手的最后一个 ACK 报文时，则会重发 FIN 关闭连接报文并等待新的ACK 报文。</li>
</ul>
</li>
</ul>
<h2 id="5-time_wait-过多有什么危害"><a class="markdownIt-Anchor" href="#5-time_wait-过多有什么危害"></a> 5 TIME_WAIT 过多有什么危害？</h2>
<p>如果<code>服务器</code>有处于 TIME-WAIT 状态的 TCP，则说明是由<code>服务器方主动</code>发起的断开请求。<br>
过多的 TIME-WAIT 状态主要的危害有两种：</p>
<ul>
<li>第一是内存资源占用；</li>
<li>第二是对端口资源的占用，一个 TCP 连接至少消耗一个本地端口；
<ul>
<li>【端口资源有限】</li>
<li><code>客户端</code>资源受限
<ul>
<li>客户端TIME_WAIT过多，就会导致端口资源被占用，因为端口就65536个，被占满就会导致无法<br>
创建新的连接。</li>
</ul>
</li>
<li><code>服务端</code>资源受限
<ul>
<li>由于一个四元组表示 TCP 连接，理论上服务端可以建立很多连接，服务端确实只监听一个端口 但<br>
是会把连接扔给处理线程，所以理论上监听的端口可以继续监听。但是线程池处理不了那么多一<br>
直不断的连接了。所以当服务端出现大量 TIME_WAIT 时，系统资源被占满时，会导致处理不过<br>
来新的连接。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="6-如何优化-time_wait"><a class="markdownIt-Anchor" href="#6-如何优化-time_wait"></a> 6 如何优化 TIME_WAIT？</h2>
<h3 id="1打开-netipv4tcp_tw_reuse-和-netipv4tcp_timestamps-选项"><a class="markdownIt-Anchor" href="#1打开-netipv4tcp_tw_reuse-和-netipv4tcp_timestamps-选项"></a> （1）打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项；</h3>
<ul>
<li>通过时间戳选项，位于tcp首部【选项】,代替2MSL的问题，识别数据包</li>
<li>reuse：复用位于Time_wait的连接
<ul>
<li><code>交易的发起方！！！</code></li>
</ul>
</li>
</ul>
<h3 id="2netipv4tcp_max_tw_buckets"><a class="markdownIt-Anchor" href="#2netipv4tcp_max_tw_buckets"></a> （2）net.ipv4.tcp_max_tw_buckets</h3>
<p>这个值默认为 18000，当系统中处于 TIME_WAIT 的连接一旦超过这个值时，系统就会将所有的 TIME_WAIT 连接状态重置。<br>
这个方法过于暴力，而且治标不治本，带来的问题远比解决的问题多，不推荐使用。</p>
<h3 id="3程序中使用-so_linger"><a class="markdownIt-Anchor" href="#3程序中使用-so_linger"></a> （3）程序中使用 SO_LINGER</h3>
<p>我们可以通过<code>设置 socket</code>选项，来设置调用 close 关闭连接行为。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">struct linger so_linger;<br>so_linger.l_onoff = 1;<br>so_linger.l_linger = 0;<br>setsockopt(s, SOL_SOCKET, SO_LINGER, &amp;so_linger,sizeof(so_linger));<br></code></pre></td></tr></table></figure>
<p>如果	<code>l_onoff 为非 0， 且l_linger 值为 0</code>，那么调用close 后，会立该发送一个<code>RST</code> 标志给对端，该TCP 连接将<code>跳过四次挥手</code>，也就跳过了TIME_WAIT 状态，直接关闭。</p>
<p>但这为跨越TIME_WAIT 状态提供了一个可能，不过是一个<code>非常危险</code>的行为，不值得提倡。</p>
<h2 id="7-如果已经建立了连接但是客户端突然出现故障了怎么办"><a class="markdownIt-Anchor" href="#7-如果已经建立了连接但是客户端突然出现故障了怎么办"></a> 7 如果已经建立了连接，但是客户端突然出现故障了怎么办？</h2>
<p>TCP 有一个机制是<code>保活机制</code>。这个机制的原理是这样的：<br>
定义一个时间段，在这个时间段内，如果没有任何连接相关的活动，TCP 保活机制会开始作用，每隔一个时间间隔，发送一个<strong>探测报文</strong>，该探测报文包含的<strong>数据非常少</strong>，如果连续几个探测报文都没有得到响应，则认为当前的 TCP 连接已经死亡，系统内核将错误信息通知给上层应用程序。</p>
<p>在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔，以下都为默认值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs ba">net.ipv4.tcp_keepalive_time=7200<br>net.ipv4.tcp_keepalive_intvl=75<br>net.ipv4.tcp_keepalive_probes=9<br></code></pre></td></tr></table></figure>
<ul>
<li>tcp_keepalive_time=7200：表示保活时间是 7200 秒（2小时），也就 2 小时内如果没有任何连接相关的活动，则会<strong>启动保活机制</strong></li>
<li>tcp_keepalive_intvl=75：表示每次<strong>检测间隔</strong> 75 秒；</li>
<li>tcp_keepalive_probes=9：表示<strong>检测 9 次无</strong>响应，认为对方是不可达的，从而中断本次的连接。</li>
<li>最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接
<ul>
<li>可以自定义</li>
</ul>
</li>
</ul>
<p>如果开启了 TCP 保活，需要考虑以下几种情况：</p>
<ul>
<li>第一种，对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应，这样 <code>TCP 保 活时间会被重置</code>，等待下一个 TCP 保活时间的到来。</li>
<li>第二种，对端程序<code>崩溃并重启</code>。当 TCP 保活的探测报文发送给对端后，对端是可以响应的，但由于没<br>
有该连接的有效信息，会产生一个 <code>RST</code> 报文，这样很快就会发现 TCP 连接已经被重置。</li>
<li>第三种，是对端程序<code>崩溃</code>，或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端<br>
后，石沉大海，没有响应，连续几次，达到保活探测次数后，TCP 会报告该 TCP 连接已经死亡。</li>
</ul>
<h1 id="四-socket编程"><a class="markdownIt-Anchor" href="#四-socket编程"></a> 四、Socket编程</h1>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/4.jpg" srcset="/img/loading.gif" lazyload alt></p>
<h2 id="1-针对-tcp-应该如何-socket-编程"><a class="markdownIt-Anchor" href="#1-针对-tcp-应该如何-socket-编程"></a> 1 针对 TCP 应该如何 Socket 编程？</h2>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/19.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li>服务端和客户端初始化 <code>socket</code> ，得到文件描述符(socket为文件形式)；</li>
<li>服务端调用<code>bind</code> ，将绑定在 IP 地址和端口;   【向OS请求 占用】</li>
<li>服务端调用 <code>listen</code> ，进行监听；</li>
<li>服务端调用 <code>accept</code>，等待客户端连接；     【持续accpet】
<ul>
<li>服务端调用 accept 时，连接成功了会返回一个已完成连接的 socket，后续用来<br>
传输数据。</li>
</ul>
</li>
<li>客户端调用 <code>connect</code> ，向服务器端的地址和端口发起连接请求；</li>
<li>服务端 <code>accept</code>返回用于传输的 <code>socket</code> 的文件描述符；</li>
<li>客户端调用 <code>write</code> 写入数据；服务端调用<code>read</code> 读取数据；</li>
<li>客户端断开连接时，会调用<code>close</code> ，那么服务端 read 读取数据的时候，就会读取到了<br>
<code>EOF</code> ，待处理完数据后，服务端调用 <code>close</code> ，表示连接关闭。</li>
</ul>
<p>所以，监听的 socket 和真正用来传送数据的 socket，是「两个」 socket，一个叫作<code>监听 socket</code>，一个叫作<code>已完成连接 socket</code>。</p>
<ul>
<li>socket = ip + 端口</li>
<li>服务端监听某个端口，客户端向这个端口请求连接；然后，服务端分配一个可用于传输的端口用于该进程的数据传输，原监听端口仍然负责监听（因为这个监听的端口相当于公开的，方便客户端请求）</li>
</ul>
<h2 id="2-listen-时候参数-backlog-的意义"><a class="markdownIt-Anchor" href="#2-listen-时候参数-backlog-的意义"></a> 2 listen 时候参数 backlog 的意义？</h2>
<p>Linux内核中会维护两个队列：</p>
<ul>
<li>未完成连接队列（SYN 队列）：接收到一个 SYN 建立连接请求，处于 SYN_RCVD 状态；</li>
<li>已完成连接队列（Accpet 队列）：已完成 TCP 三次握手过程，处于 ESTABLISHED 状态；</li>
</ul>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/20.jpg" srcset="/img/loading.gif" lazyload alt></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> <span class="hljs-title function_">listen</span> <span class="hljs-params">(<span class="hljs-type">int</span> socketfd, <span class="hljs-type">int</span> backlog)</span><br></code></pre></td></tr></table></figure>
<ul>
<li>参数一 socketfd 为 socketfd <strong>文件描述符</strong></li>
<li>参数二 backlog，这参数在历史版本有一定的变化
<ul>
<li>早期 Linux 内核 backlog 是 SYN 队列大小，也就是未完成的队列大小。</li>
<li>Linux 内核 2.2 之后，backlog 变成 accept 队列，也就是已完成连接建立的队列长度，所以现在通常认为 backlog 是 accept 队列。</li>
<li>上限值是内核参数 <code>somaxconn</code>的大小，也就说 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>a</mi><mi>c</mi><mi>c</mi><mi>p</mi><mi>e</mi><mi>t</mi><mi mathvariant="normal">队</mi><mi mathvariant="normal">列</mi><mi mathvariant="normal">长</mi><mi mathvariant="normal">度</mi><mo>=</mo><mi>m</mi><mi>i</mi><mi>n</mi><mo stretchy="false">(</mo><mi>b</mi><mi>a</mi><mi>c</mi><mi>k</mi><mi>l</mi><mi>o</mi><mi>g</mi><mo separator="true">,</mo><mi>s</mi><mi>o</mi><mi>m</mi><mi>a</mi><mi>x</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">accpet 队列长度 = min(backlog,somaxconn)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.80952em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">a</span><span class="mord mathdefault">c</span><span class="mord mathdefault">c</span><span class="mord mathdefault">p</span><span class="mord mathdefault">e</span><span class="mord mathdefault">t</span><span class="mord cjk_fallback">队</span><span class="mord cjk_fallback">列</span><span class="mord cjk_fallback">长</span><span class="mord cjk_fallback">度</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">i</span><span class="mord mathdefault">n</span><span class="mopen">(</span><span class="mord mathdefault">b</span><span class="mord mathdefault">a</span><span class="mord mathdefault">c</span><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">s</span><span class="mord mathdefault">o</span><span class="mord mathdefault">m</span><span class="mord mathdefault">a</span><span class="mord mathdefault">x</span><span class="mord mathdefault">c</span><span class="mord mathdefault">o</span><span class="mord mathdefault">n</span><span class="mord mathdefault">n</span><span class="mclose">)</span></span></span></span>。</li>
</ul>
</li>
</ul>
<h2 id="3-accept-发生在三次握手的哪一步"><a class="markdownIt-Anchor" href="#3-accept-发生在三次握手的哪一步"></a> 3 accept 发生在三次握手的哪一步？</h2>
<p><img src="/img/TCP%E6%8F%A1%E6%89%8B.assets/21.jpg" srcset="/img/loading.gif" lazyload alt></p>
<ul>
<li><code>客户端</code>的协议栈向服务器端发送了 SYN 包，并告诉服务器端当前发送序列号 client_isn，客户端进入 <code>SYN_SENT</code> 状态；</li>
<li><code>服务器端</code>的协议栈收到这个包之后，和客户端进行 ACK 应答，应答的值为 client_isn+1，表示对SYN 包 client_isn 的确认，同时服务器也发送一个 SYN 包，告诉客户端当前我的发送序列号为server_isn，服务器端进入 <code>SYN_RCVD</code>状态；</li>
<li><code>客户端</code>协议栈收到 ACK 之后，使得应用程序从<code>connect 调用返回</code>，表示客户端到服务器端的单向连接建立成功，客户端的状态为 ESTABLISHED，同时客户端协议栈也会对服务器端的 SYN 包进行应答，应答数据为 server_isn+1；</li>
<li>应答包到达<code>服务器端</code>后，服务器端协议栈使得 <code>accept 阻塞调用返回</code>，这个时候服务器端到客户端的单向连接也建立成功，服务器端也进入 ESTABLISHED 状态。</li>
</ul>
<p><strong>可以得知客户端 connect 成功返回是在第二次握手，服务端 accept 成功返回是在三次握手成功之后</strong></p>
<h2 id="4-客户端调用-close-了连接是断开的流程是什么"><a class="markdownIt-Anchor" href="#4-客户端调用-close-了连接是断开的流程是什么"></a> 4 客户端调用 close 了，连接是断开的流程是什么？</h2>
<ul>
<li>客户端调用 close ，表明客户端没有数据需要发送了，则此时会向服务端发送 FIN 报文，进入 FIN_WAIT_1 状态；</li>
<li><code>服务端</code>接收到了 FIN 报文，TCP 协议栈会<code>为 FIN 包插入一个文件结束符 EOF</code>到<code>接收缓冲区</code>中，(服务端)应用程序可以通过 <code>read</code> 调用来感知这个 FIN 包。这个 EOF 会被放在已排队等候的其他已接收的数据之后，这就意味着服务端需要处理这种异常情况，因为 EOF 表示在该连接上再无额外数据到达。此时，服务端进入 CLOSE_WAIT 状态；</li>
<li>接着，当处理完数据后，自然就会读到 EOF ，于是也调用 close <code>关闭它的套接字</code>，这会使得<code>服务端</code>会发出一个 FIN 包，之后处于 LAST_ACK 状态；</li>
<li><code>客户端</code>接收到服务端的 FIN 包，并发送 ACK 确认包给服务端，此时客户端将进入 TIME_WAIT 状态；</li>
<li>服务端收到 ACK 确认包后，就进入了最后的 CLOSE 状态；</li>
<li>客户端经过 2MSL 时间之后，也进入 CLOSE 状态；   【发起者最后进入CLOSE】</li>
</ul>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/%E7%BD%91%E7%BB%9C/" class="category-chain-item">网络</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/%E9%9D%A2%E8%AF%95/">#面试</a>
      
        <a href="/tags/TCP/">#TCP</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>TCP握手</div>
      <div>http://example.com/2022/04/02/TCP握手/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>叶飞</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年4月2日</div>
        </div>
      
      
      <div class="license-meta-item">
        <div>许可协议</div>
        <div>
          
            
            
              <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
              <span class="hint--top hint--rounded" aria-label="BY - 署名">
                <i class="iconfont icon-by"></i>
              </span>
              </a>
            
          
        </div>
      </div>
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/04/02/TCP%E9%87%8D%E4%BC%A0-%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%EF%BC%9A%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6-%E6%8B%A5%E5%A1%9E%E6%8E%A7%E5%88%B6/" title="TCP重传-滑动窗口：流量控制-拥塞控制">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">TCP重传-滑动窗口：流量控制-拥塞控制</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/04/02/HTTP%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98/" title="HTTP常见面试题">
                        <span class="hidden-mobile">HTTP常见面试题</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  


  
  









    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      headingSelector : CONFIG.toc.headingSelector || 'h1,h2,h3,h4,h5,h6',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      collapseDepth   : CONFIG.toc.collapseDepth || 0,
      scrollSmooth    : true,
      headingsOffset  : -boardTop
    });
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.10/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  
      <script>
        if (!window.MathJax) {
          window.MathJax = {
            tex    : {
              inlineMath: { '[+]': [['$', '$']] }
            },
            loader : {
              load: ['ui/lazy']
            },
            options: {
              renderActions: {
                insertedScript: [200, () => {
                  document.querySelectorAll('mjx-container').forEach(node => {
                    let target = node.parentNode;
                    if (target.nodeName.toLowerCase() === 'li') {
                      target.parentNode.classList.add('has-jax');
                    }
                  });
                }, '', false]
              }
            }
          };
        } else {
          MathJax.startup.document.state(0);
          MathJax.texReset();
          MathJax.typeset();
          MathJax.typesetPromise();
        }
      </script>
    

  <script  src="https://lib.baomitu.com/mathjax/3.2.1/es5/tex-mml-chtml.js" ></script>

  <script  src="/js/local-search.js" ></script>

  <script defer src="/js/leancloud.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body>
</html>
